package com.zjsos.db.hibernate;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.StringUtils;

import com.zaxxer.hikari.HikariDataSource;
import com.zjsos.utils.common.PropertiesUtil;

/**
 * 开发者通过Hibernate的SessionFactory操作数据库
 * 
 * SessionFactory通过LocalSessionFactoryBean生成
 * 
 * 我这里写个LocalSessionFactoryBean的建造者方便开发人员构造LocalSessionFactoryBean
 * 
 * @author sharlot2050@foxmail.com
 * @date 2019年8月24日22:10:41
 * 
 */
public class LocalSessionFactoryBeanBuilder {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	/**
	 * 
	 * 如有需要，你可以通过如下方式获取spring.datasource的配置:
	 * 			
	 * 			@Autowired
	 * 			DataSourceProperties dataSourceProperties;
	 * 
	 * 如有需要，你可以通过如下方式获取spring.jpa的配置
	 * 
	 * 			@Autowired
	 * 			JpaProperties jpaProperties;
	 * 
	 * @param dataSourceProperties
	 * @param jpaProperties
	 * @throws Exception
	 */
	public LocalSessionFactoryBeanBuilder(DataSourceProperties dataSourceProperties, JpaProperties jpaProperties) throws Exception {
//		this.datasourceProperties = dataSourceProperties;
//		this.jpaProperties = jpaProperties;
		this.hibernateProperties = decudeHibernateProperties(dataSourceProperties, jpaProperties);
		this.dataSource = decudeDataSource(dataSourceProperties);
		this.packagesToScan = decudePackagesToScanFromHibernateProperties(this.hibernateProperties);
		this.physicalNamingStrategy = decudePhysicalNamingStrategy(jpaProperties);
	}
	/**
	 * 
	 * 创建LocalSessionFactoryBean对象
	 * 
	 * @Author sharlot2050@foxmail.com
	 * @Date 2019年8月26日
	 * @Version 1.00
	 */
	public LocalSessionFactoryBean create() throws Exception{
		LocalSessionFactoryBean localSessionFactoryBean = sessionFactory();
//		hibernateTransactionManager(localSessionFactoryBean.getObject());
		System.out.println(this.packagesToScan[0]);
		return localSessionFactoryBean;
	}
	/**
	 * 扫包
	 * 
	 * 		如果配置com.zjsos，效果类似com.zjsos.**。
	 * 		因此com.zjsos.entity.cas.ZUser.java也会被扫描到
	 */
	String[] packagesToScan;
	/**
	 * 
	 * springboot的yml的好像没有扫包配置，我们自己定义一个
	 * 
	 * @Author sharlot2050@foxmail.com
	 * @Date 2019年8月26日
	 * @Version 1.00
	 */
	public static String[] decudePackagesToScanFromHibernateProperties(Properties hibernateProperties) throws Exception {
		if(hibernateProperties.containsKey("hibernate." + "packages-to-scan") &&
				StringUtils.hasText(hibernateProperties.get("hibernate." + "packages-to-scan")+"")) {
			return (hibernateProperties.get("hibernate." + "packages-to-scan")+"").split(",");
		}
		throw new Exception("需要先配置包扫描  ***.jpa.properties.hibernate.packages-to-scan ， 多个包之间用逗号隔开。 ");
	}
	//    public LocalSessionFactoryBeanBuilder datasourceProperties(Properties datasourceProperties) {
	//        this.datasourceProperties = datasourceProperties;
	//        return this;
	//    }
	//
	//    public LocalSessionFactoryBeanBuilder hibernateProperties(Properties hibernateProperties) {
	//        this.hibernateProperties = hibernateProperties;
	//        return this;
	//    }
	//
	//    public LocalSessionFactoryBeanBuilder packagesToScan(String[] packagesToScan) {
	//        this.packagesToScan = packagesToScan;
	//        return this;
	//    }

	public LocalSessionFactoryBeanBuilder physicalNamingStrategy(PhysicalNamingStrategy physicalNamingStrategy) {
		this.physicalNamingStrategy = physicalNamingStrategy;
		return this;
	}

	private DataSource dataSource;
	/**
	 * 依据spring.datasource生成DataSource
	 * @Author sharlot2050@foxmail.com
	 * @Date 2019年8月26日
	 * @Version 1.00
	 */
	private DataSource decudeDataSource(DataSourceProperties dataSourceProperties){

		if(dataSource == null) {
			HikariDataSource dataSource = new HikariDataSource();
			dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
			dataSource.setJdbcUrl(dataSourceProperties.getUrl());
			dataSource.setUsername(dataSourceProperties.getUsername());
			dataSource.setPassword(dataSourceProperties.getPassword());
			return dataSource;
		}else {
			return dataSource;
		}
	}
	/**
	 * 映射spring.datasource的配置
	 */
//	DataSourceProperties datasourceProperties;
	/**
	 * 映射spring.jpa的配置
	 */
//	JpaProperties jpaProperties;
	/**
	 * 映射spring.jpa.hibernate
	 */
	private Properties hibernateProperties;
	/**
	 * 
	 * 依据spring.datasource、spring.jpa生成HibernateProperties
	 * @Author sharlot2050@foxmail.com
	 * @Date 2019年8月26日
	 * @Version 1.00
	 */
	private Properties decudeHibernateProperties(DataSourceProperties dataSourceProperties, JpaProperties jpaProperties) {
		return PropertiesUtil.map2properties(jpaProperties.getHibernateProperties(decudeDataSource(dataSourceProperties)));
	}
	/**
	 * 数据库表\字段和实体类\成员变量的映射策略
	 */
	PhysicalNamingStrategy physicalNamingStrategy;
	private PhysicalNamingStrategy decudePhysicalNamingStrategy(JpaProperties jpaProperties) throws Exception{
		if(jpaProperties.getHibernate().getNaming() == null &&
				jpaProperties.getHibernate().getNaming().getPhysicalStrategy() == null){
			return new com.zjsos.db.hibernate.MyPhysicalNamingStrategy();
		}else{
			Class myPhysicalNamingStrategyClazz;
			try {
				myPhysicalNamingStrategyClazz = Class.forName(jpaProperties.getHibernate().getNaming().getPhysicalStrategy());
			} catch (Exception e) {
				throw new Exception(
						"class [" + jpaProperties.getHibernate().getNaming().getPhysicalStrategy()+ "] " +
								"from spring.jpa.hibernate.naming.physical-strategy is not found");
			}
			return (PhysicalNamingStrategy)myPhysicalNamingStrategyClazz.newInstance();
		}
	}

	private LocalSessionFactoryBean sessionFactory() throws Exception {
		
		LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
		sessionFactoryBean.setDataSource(this.dataSource);
		sessionFactoryBean.setHibernateProperties(this.hibernateProperties);
		sessionFactoryBean.setPackagesToScan(this.packagesToScan);
		sessionFactoryBean.setPhysicalNamingStrategy(this.physicalNamingStrategy);
		return sessionFactoryBean;
	}

}
